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.
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 \
--releaseBoth 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 htmlThis 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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.