Frontend Development 19 min read

Optimizing Flutter Web Startup: main.dart.js Splitting, CanvasKit Loading, and Font Loading Optimization

By splitting the main.dart.js bundle with deferred components, serving CanvasKit assets and Google fonts from a private CDN, and optionally using the HTML renderer during development, Flutter Web apps can cut their startup time by five to six seconds, achieving sub‑second load performance.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Optimizing Flutter Web Startup: main.dart.js Splitting, CanvasKit Loading, and Font Loading Optimization

In a previous article we introduced the complete workflow for building and deploying a Flutter Web project from zero to production. After deployment we observed that the startup time of the web version is noticeably slower than a traditional web page, especially the initial load of the main.dart.js bundle.

This article focuses on three optimization techniques for Flutter Web projects using Flutter SDK 3.19.0:

Splitting the main.dart.js bundle with deferred-components .

Loading CanvasKit assets ( canvaskit.js and canvaskit.wasm ) from a custom CDN.

Downloading and serving Google font files from a private CDN to avoid latency.

1. Adding the deferred‑components dependency

Insert the following into pubspec.yaml under the flutter section:

flutter:
  deferred-components:

2. Implementing deferred loading

Choose a widget that can be loaded lazily and import it with the deferred keyword:

import './widget/me_read_achievement_widget.dart' deferred as achievement;

In the state class, start loading the library in initState :

late Future
_libraryFuture;

@override
void initState() {
  super.initState();
  _libraryFuture = achievement.loadLibrary();
}

Build the UI with a FutureBuilder that shows the widget once the library is loaded:

@override
Widget build(BuildContext context) {
  return FutureBuilder
(
    future: _libraryFuture,
    builder: (context, snapshot) {
      if (snapshot.connectionState == ConnectionState.done) {
        return achievement.MeReadAchievementWidget();
      }
      return const CircularProgressIndicator();
    },
  );
}

After rebuilding, the output contains an additional file main.dart.js_1.part.js , which is the deferred part. When the page loads, only main.dart.js is fetched; the split part is downloaded only when the user navigates to the corresponding screen, reducing the initial payload.

3. CanvasKit assets loading

CanvasKit (a WebAssembly‑based Skia implementation) consists of canvaskit.js and canvaskit.wasm . These files are normally fetched from Google’s servers, which can add a few seconds to the startup time. To serve them from your own CDN, configure the engine initializer:

_flutter.loader.loadEntrypoint({
  onEntrypointLoaded: async function(engineInitializer) {
    let appRunner = await engineInitializer.initializeEngine({
      canvasKitBaseUrl: YOUR_CANVASKIT_CDN_BASE_URL
    });
    await appRunner.runApp();
  }
});

Alternatively, pass the URL via a build flag:

flutter build web -t lib/main_test.dart \
  --no-tree-shake-icons \
  --dart-define=FLUTTER_WEB_CANVASKIT_URL=YOUR_CANVASKIT_CDN_BASE_URL \
  --release

Both methods make the web app load CanvasKit assets from the specified CDN, cutting the download time from 2‑3 seconds (Google) to a few milliseconds (local CDN).

4. Font loading optimization

When CanvasKit rendering is used, many Noto fonts are downloaded from fonts.gstatic.com . To avoid this latency, extract the font URLs from the compiled main.dart.js (they appear in a large array of .ttf paths) and download them in bulk. A Python script can automate the process:

# coding=utf-8
import os, requests
original_text = '... (large JS string) ...'
font_list_content = original_text.split('"')
font_list = []
for font in font_list_content:
    if font.endswith('.ttf'):
        font_list.append(font)
host = 'https://fonts.gstatic.com/s/'
fonts = 'fonts/'
path = ''
for font in font_list:
    folder_list = font.split('/')
    for folder in folder_list:
        if folder.endswith('.ttf'):
            url = host + path + folder
            r = requests.get(url)
            with open(fonts + path + folder, 'wb') as file:
                file.write(r.content)
            path = ''
        else:
            path += folder + '/'
            os.makedirs(fonts + path, exist_ok=True)

Upload the downloaded fonts to your CDN and replace the original Host URLs in main.dart.js with your CDN address. After redeploying, the font download time drops from seconds to milliseconds.

5. Development‑time rendering mode

During development you can avoid loading CanvasKit (and thus the fonts) by switching to the HTML renderer:

flutter run -d Chrome -t lib/main_test.dart --web-renderer html

This renders using the browser’s native text layout engine, eliminating the extra font fetches.

Summary

By applying main.dart.js splitting via deferred-components , serving CanvasKit assets from a custom CDN, and pre‑hosting required font files, a Flutter Web project can reduce its startup time by 5‑6 seconds, bringing the perceived load time down to the sub‑second (often millisecond) range.

performance optimizationcdnFlutter WebCanvasKitDeferred ComponentsFont Loading
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.